<script setup lang="ts">
import { ArrowDown, Close, Eleme, MoreFilled } from '@element-plus/icons-vue';
import { listen, UnlistenFn } from '@tauri-apps/api/event';
import { stat } from '@tauri-apps/plugin-fs';
import { showInfoNotification, showWarningNotification } from 'my-vue-ui-core/notification';
import { BrowserStorage } from 'my-web-core/storage';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';

import AnalysisParameter from '@/features/home/models/analysis-parameter';
import LocaleName from '@/shared/constants/locale-name';
import StorageKey from '@/shared/constants/storage-key';

defineProps<{
  processing: boolean;
}>();

const emit = defineEmits<{
  (e: 'start-processing', param: AnalysisParameter): void;
  (e: 'cancel-processing'): void;
}>();

const { t, locale } = useI18n();

const selectedPaths = ref<string[]>([]);

// For size range selection.
const minSize = ref<number>();
const maxSize = ref<number>();
const enableMinSize = ref(false);
const enableMaxSize = ref(false);

// For file type selection.
const enableIncludeFileTypes = ref(false);
const enableExcludeFileTypes = ref(false);
const includeFileTypes = ref<string[]>();
const excludeFileTypes = ref<string[]>();

// Computes color based on any advanced options enabled.
const optionTitleColor = computed(() => {
  if ((enableMinSize && minSize.value) || (enableMaxSize && maxSize.value)) {
    return '#409eff';
  }

  return 'grey';
});

// Computes size range info.
const optionTitleSizeRangeInfo = computed(() => {
  let info = 'File Size';

  if (minSize.value) {
    info = `${minSize.value} MB <= ` + info;
  }

  if (maxSize.value) {
    info += ` < ${maxSize.value} MB`;
  }

  if (!minSize.value && !maxSize.value) {
    info = '';
  }

  return info;
});

// Computes file type filter info for display on collapse title.
const optionFileTypeFilterInfo = computed(() => {
  let info = '';

  if (
    enableIncludeFileTypes.value &&
    includeFileTypes.value?.length &&
    includeFileTypes.value?.length > 0
  ) {
    info += `${t('includedFileTypes')} [${includeFileTypes.value?.join(', ')}]`;
  }

  if (
    enableExcludeFileTypes.value &&
    excludeFileTypes.value?.length &&
    excludeFileTypes.value?.length > 0
  ) {
    if (info != '') {
      info += ' / ';
    }

    info += `Excluded File Types: [${excludeFileTypes.value?.join(', ')}]`;
  }

  return info;
});

// Loads saved locale.
let currentLocale = ref(BrowserStorage.loadLocalData(StorageKey.locale));

// Computes locale title for display in the drop down title.
const currentLocaleTitle = computed(() => (currentLocale.value == LocaleName.zhCn ? '中文' : 'EN'));

// Listener of drag & drop and will be unlistened in unmounted phase.
let unlistenDragDropFn: UnlistenFn;

onMounted(() => {
  // Sets up listener for drag & drop.
  listenDragDrop();
});

onUnmounted(() => {
  // Unlistens for drag & drop.
  unlistenDragDropFn?.();
});

/**
 * Changes current locale based on user selection.
 */
function changeLocale(localeParam: string): void {
  currentLocale.value = localeParam;
  locale.value = localeParam;
  BrowserStorage.saveLocalData(StorageKey.locale, localeParam);
}

/**
 * Creates listener on drag & drop.
 */
async function listenDragDrop(): Promise<void> {
  unlistenDragDropFn = await listen('tauri://drag-drop', async (event) => {
    const newAddedPaths = (event.payload as any).paths;
    addPaths(newAddedPaths);
  });
}

/**
 * Adds new added paths to current selected paths. We need to check if this path is already included in selected paths.
 */
async function addPaths(paths: string[]): Promise<void> {
  const finalAddedPaths: string[] = [];
  let matchedPaths: string[];

  for (let i = 0; i < paths.length; i++) {
    // Gets metadata of this path to determine it is file or directory.
    const metadata = await stat(paths[i]);

    if (metadata.isDirectory) {
      matchedPaths = selectedPaths.value.filter(
        (p) => paths[i].startsWith(p) && paths[i].length >= p.length,
      );

      if (matchedPaths.length > 0) {
        // newAddedPath: 'D:\Temp\code'
        // currentPaths: ['D:\Temp']
        // Means already included in this selected path. We just need to ignore.
        showInfoNotification(
          t('alreadyIncluded', { path: paths[i], matchedPath: matchedPaths[i] }),
        );
        continue;
      }

      matchedPaths = selectedPaths.value.filter(
        (p) => p.startsWith(paths[i]) && p.length >= paths[i].length,
      );

      if (matchedPaths.length > 0) {
        // newAddedPath: 'D:\Temp'
        // currentPaths: ['D:\Temp\code']
        // Means added one included the one in this selected path. We just need to replace it
        for (let j = 0; j < matchedPaths.length; j++) {
          const currentPathIndex = selectedPaths.value.indexOf(matchedPaths[j]);
          selectedPaths.value[currentPathIndex] = paths[i];

          showInfoNotification(
            t('replacedBy', { matchedPath: matchedPaths[j], path: paths[i] }),
          );
        }

        continue;
      }

      finalAddedPaths.push(paths[i]);
    } else if (metadata.isFile) {
      matchedPaths = selectedPaths.value.filter((p) => paths[i].startsWith(p));

      if (matchedPaths.length == 0) {
        finalAddedPaths.push(paths[i]);
      } else {
        // newAddedPath: D:\Temp\code\foo.txt
        // selectedPaths: ['D:\Temp']
        // Means already included in this selected path. We just need to ignore.
        showInfoNotification(
          t('alreadyIncluded', { path: paths[i], matchedPath: matchedPaths[0] }),
        );
        continue;
      }
    }
  }

  selectedPaths.value.push(...finalAddedPaths);

  // Scenario:
  // If selected paths are ['D:\a', 'D:\b', 'D:\c'] and all replaced by 'D:\' then duplicate paths exist.
  // Then we use Set to remove duplicates.
  selectedPaths.value = [...new Set(selectedPaths.value)];
}

/**
 * Removes single selected path tag.
 */
function removeSelectedPath(tag: string): void {
  selectedPaths.value.splice(selectedPaths.value.indexOf(tag), 1);
}

/**
 * Emits event with parameter.
 */
function startProcessing(): void {
  // Checks file size range option.
  if (
    enableMinSize.value &&
    enableMaxSize.value &&
    minSize.value &&
    maxSize.value &&
    minSize.value > maxSize.value
  ) {
    showWarningNotification(t('warningMinSizeLargerThanMaxSize'));

    return;
  }

  emit('start-processing', {
    paths: selectedPaths.value,
    minSize: enableMinSize.value ? minSize.value : null,
    maxSize: enableMaxSize.value ? maxSize.value : null,
    includeFileTypes: enableIncludeFileTypes.value ? includeFileTypes.value : null,
    excludeFileTypes: enableExcludeFileTypes.value ? excludeFileTypes.value : null,
  });
}
</script>

<template>
  <div class="toolbar">
    <el-button
      type="primary"
      class="main-button"
      v-if="!processing"
      :icon="Eleme"
      :disabled="selectedPaths.length == 0"
      @click="startProcessing"
      >{{ t('start') }}</el-button
    >
    <el-button
      type="warning"
      class="main-button"
      v-else
      :icon="Close"
      @click="emit('cancel-processing')"
      >{{ t('cancel') }}</el-button
    >
    <el-button
      :icon="Close"
      @click="selectedPaths.length = 0"
      circle
      :disabled="selectedPaths.length == 0 || processing"
      :title="t('clearSelectedPaths')"
    />
    <div class="tag-container">
      <el-tag
        v-for="path in selectedPaths"
        :key="path"
        @close="removeSelectedPath(path)"
        type="primary"
        :closable="!processing"
      >
        {{ path }}
      </el-tag>
      <span style="margin-left: 2px">{{ t('dragDropHere') }}</span>
    </div>
    <div>
      <el-dropdown>
        <span class="drop-down-title">
          {{ currentLocaleTitle }}
          <el-icon>
            <arrow-down />
          </el-icon>
        </span>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item @click="changeLocale(LocaleName.zhCn)">
              <span :class="{ 'selected-menu-item': currentLocale == LocaleName.zhCn }">中文</span>
            </el-dropdown-item>
            <el-dropdown-item @click="changeLocale(LocaleName.en)">
              <span :class="{ 'selected-menu-item': currentLocale == LocaleName.en }">English</span>
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
  </div>
  <el-collapse>
    <el-collapse-item>
      <template #title>
        <div class="option-info-container">
          <el-icon size="20" :color="optionTitleColor">
            <MoreFilled />
          </el-icon>
          <span>{{ optionTitleSizeRangeInfo }}</span>
          <span>{{ optionFileTypeFilterInfo }}</span>
        </div>
      </template>
      <div class="advanced-option-container">
        <div class="option-select-container">
          <div class="option-select-container">
            <el-checkbox
              v-model="enableMinSize"
              :label="t('largerThan')"
              size="small"
              :disabled="processing"
            />
            <el-input-number
              v-model="minSize"
              :disabled="!enableMinSize || processing"
              :min="0"
              size="small"
            >
              <template #suffix>
                <span>MB</span>
              </template>
            </el-input-number>
          </div>
          <div style="margin-left: 20px" class="option-select-container">
            <el-checkbox
              v-model="enableMaxSize"
              :label="t('smallerThan')"
              size="small"
              :disabled="processing"
            />
            <el-input-number
              v-model="maxSize"
              :disabled="!enableMaxSize || processing"
              :min="0"
              size="small"
            >
              <template #suffix>
                <span>MB</span>
              </template>
            </el-input-number>
          </div>
        </div>
        <div style="height: 10px"></div>
        <div class="option-select-container">
          <div class="option-select-container">
            <el-checkbox
              v-model="enableIncludeFileTypes"
              :label="t('includedFileTypes')"
              size="small"
              :disabled="processing"
            />
            <el-input-tag
              v-model="includeFileTypes"
              :disabled="!enableIncludeFileTypes"
              trigger="Space"
              :placeholder="t('fileExtension')"
              size="small"
            />
          </div>
          <div class="option-select-container">
            <el-checkbox
              v-model="enableExcludeFileTypes"
              :label="t('excludedFileTypes')"
              size="small"
              :disabled="processing"
            />
            <el-input-tag
              v-model="excludeFileTypes"
              :disabled="!enableExcludeFileTypes"
              trigger="Space"
              :placeholder="t('fileExtension')"
              size="small"
            />
          </div>
        </div>
      </div>
    </el-collapse-item>
  </el-collapse>
</template>

<style scoped>
:deep(.el-collapse-item__header) {
  padding-left: 12px;
}

.toolbar {
  background-color: #efefef;
  display: flex;
  align-items: center;
  padding: 6px 6px 6px 10px;
}

.main-button {
  width: 80px;
}

.tag-container {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-left: 6px;
  max-height: 50px;
  overflow-y: auto;
  flex: 1;
  align-items: center;
  font-size: 0.8rem;
  color: #898585;
}

.drop-down-title {
  font-size: 0.8rem;
  color: #a1a1a3;
}

.selected-menu-item {
  color: #409eff;
}

.advanced-option-container {
  padding-left: 12px;
}

.option-info-container {
  display: flex;
  align-items: center;
  gap: 10px;
  color: gray;
  font-size: 0.8rem;
}

.option-select-container {
  display: flex;
  gap: 10px;
  font-size: 0.8rem;
  color: gray;
  align-items: center;
}

.selected-path {
  color: #999;
  margin-left: 10px;
  font-size: 0.8rem;
}
</style>
